/*****************************************************************************************
 * ETS
 * 
 * Cours:		LOG121
 * Session:		A2013
 * Groupe:		04
 * Projet:		Laboratoire #4
 * Etudiants:	Frederic-Simon Mimeault
 * 				Francisco Felipe Quilodran
 * 				Guillaume Longtin
 * 				William Proulx
 * 
 * Professeur:	Francis Cardinal
 * 
 * Fichier:		GestionnaireCommande.java
 * 
 * Date de creation:	2013-11-23
 * Date dern. modif.:	2013-11-29
 * ****************************************************************************************
 * Historique des modifications
 * ****************************************************************************************
 * 2013-11-23 Version initiale
 * 2013-11-29 Implementation de la gestion des commandes
 *****************************************************************************************/

package controleur;

import java.awt.Point;
import java.util.Stack;

import modele.AbstractCommande;
import modele.AbstractFabriqueCommande;
import modele.CmdTranslation;
import modele.CmdZoom;
import modele.Perspective;

/**
 * Cette classe est le contrôleur des commandes.
 * C'est le patron de conception Singleton qui est en place dans cette classe.
 * GestionnaireCommande est un Singleton.
 * @author Guillaume Longtin
 */
public class GestionnaireCommande {
	
	private Stack<AbstractCommande> commandesEffectues=new Stack<AbstractCommande>();
	private Stack<AbstractCommande> commandesDefaites=new Stack<AbstractCommande>();
  
	//http://fr.wikipedia.org/wiki/Singleton_%28patron_de_conception%29
	//*****************************************************************************
	 private static volatile GestionnaireCommande instance = null;
	 
	 /**
	  * Contructeur de la classe GestionnaireCommande qui contruit un le contrôleur des commandes.
	  */
	 private GestionnaireCommande() {
		 
         super();
         
     }
	 
     /**
      * Méthode permettant de renvoyer une instance de la classe Singleton.
      * @return Retourne l'instance du singleton.
      */
     public final static GestionnaireCommande getInstance() {
         //Le "Double-Checked Singleton"/"Singleton doublement vérifié" permet 
         //d'éviter un appel coûteux à synchronized, 
         //une fois que l'instanciation est faite.
         if (GestionnaireCommande.instance == null) {
            
        	 synchronized(GestionnaireCommande.class) {
        		 
        		 if (GestionnaireCommande.instance == null) {
        			 
        			 GestionnaireCommande.instance = new GestionnaireCommande();
        			 
        		 }
        		 
             }
         }
         
         return GestionnaireCommande.instance;
         
     }
     
    /**
     * Méthode qui exécute une nouvelle commande puis l'ajoute à la pile de
     * commandes exécutées.
     * @param cmd Nouvelle commande à exécuter
     */
	public void ajouterCommande(AbstractCommande cmd) {
		
		cmd.execute();
		this.commandesEffectues.add(cmd);
		
	}
	
	/**
	 * Méthode qui annule la dernière commande executée puis la retire
	 * de la pile des commandes executées et la transfère dans la pile des
	 * commandes annulées.
	 */
	public void defaireCommande() {
		
		AbstractCommande tmpCmd = this.commandesEffectues.pop();
		tmpCmd.undo();
		this.commandesDefaites.add(tmpCmd);
		
	}
	
	/**
	 * Methode qui réexecute la dernière commande annulée puis la
	 * retire de la pile des commandes annulées et la transfère dans la
	 * pile des commandes executées.
	 */
	public void refaireCommande() {
		
		AbstractCommande tmpCmd = this.commandesDefaites.pop();
		tmpCmd.execute();
		this.commandesEffectues.add(tmpCmd);
		
	}
	
	/**
	 * Méthode de communication de la vue avec la fabrique pour obtenir une commande de translation.
	 * @param perspective Perspective à modifier
	 * @param deplacementX Valeur de déplacement en X
	 * @param deplacementY Valeur de déplacement en Y
	 * @return Nouvelle Commande Translation
	 */
	public void genererCmdTranslation(Perspective perspective, int deplacementX, int deplacementY) {
		
		int mvmtX = deplacementX;
		int mvmtY = deplacementY;
		
		int maxX = perspective.getLimiteX();
		int maxY = perspective.getLimiteY();
		
		int actualPosX = perspective.getCoordonneeX();
		int actualPosY = perspective.getCoordonneeY();
		
		if(deplacementX > 0) {
			
			if((actualPosX + mvmtX) > maxX) {
				
				mvmtX = maxX - actualPosX;
			}
			
		} else {
			
			if((actualPosX + mvmtX) < 0) {
				
				mvmtX = 0 - actualPosX;
				
			}
			
		}
		
		if(deplacementY > 0) {
			
			if((actualPosY + mvmtY) > maxY) {
				
				mvmtY = maxY - actualPosY;
				
			}
			
		} else {
			
			if((actualPosY + mvmtY) < 0) {
				mvmtY = 0 - actualPosY;
			}
			
		}
		
		if((mvmtX + mvmtY) != 0) {
			
			CmdTranslation tmpCmd = AbstractFabriqueCommande.genererCommandeTranslation(perspective, mvmtX, mvmtY);
			this.ajouterCommande(tmpCmd);
			
		}
	}
	
	/**
	 * Méthode de communication de la vue avec la fabrique pour obtenir une commande de zoom.
	 * @param perspective Perspective à modifier
	 * @param modificationEchelle Valeur du zoom
	 * @return Nouvelle Commande Zoom
	 */
	public void genererCmdZoom(Perspective perspective, int modificationEchelle) {
		
		if(perspective.getEchelle() + modificationEchelle> 9 && perspective.getEchelle() + modificationEchelle < 101) {
			
			AbstractCommande temp=AbstractFabriqueCommande.genererCommandeZoom(perspective, modificationEchelle);
			this.ajouterCommande(temp);
			
		}
		
	}
}
